#ifndef SIMFRAMEWORK_JSON_JSON_NODE_H
#define SIMFRAMEWORK_JSON_JSON_NODE_H

#include <string>
#include <hash_map>
#include <vector>

#include "interfaces/IJsonNode.h"

namespace simframework
{
	namespace json
	{

		template <class D>
		class JsonNode : public IJsonNode
		{
		public:
			virtual ~JsonNode(){}	

			template <typename T>
			T getValue(int index) const
			{
				const D* d = static_cast<const D*>(this);
				return d->getValueByIndex<T>(index);
			}

			template <typename T>
			T getValue(const std::wstring& key) const
			{
				const D* d = static_cast<const D*>(this);
				return d->getValueByKey<T>(key);
			}
			/*
			template <typename T>
			T getValueByKey(const std::wstring& key);*/

		};

		class JsonObjectNode : public JsonNode<JsonObjectNode>
		{
		public:
			JsonObjectNode(const std::wstring& value);

			virtual void addNode(IJsonNode* value);
			virtual const std::wstring& getKey() const;

			const JsonObjectNode* getInternalValue() const;

			virtual ~JsonObjectNode();

			template <typename T>
			T getValueByKey(const std::wstring& key) const;

		private:
			std::wstring m_key;
			stdext::hash_map<std::wstring, IJsonNode*> m_childList;
		};

		class JsonArrayNode : public JsonNode<JsonArrayNode>
		{
		public:
			JsonArrayNode(const std::wstring& value);

			virtual void addNode(IJsonNode* value);
			virtual const std::wstring& getKey() const;

			template <typename T>
			T getValueByIndex(int index) const;

			const JsonArrayNode* getInternalValue() const;

			size_t size() const;

			virtual ~JsonArrayNode();

		private:
			std::wstring m_key;
			std::vector<IJsonNode*> m_childList;
		};


		class JsonStringNode : public JsonNode<JsonStringNode>
		{
		public:
			JsonStringNode(const std::wstring& key, const std::wstring& value);

			virtual const std::wstring& getKey() const;

			const std::wstring& getInternalValue() const ;

		private:
			std::wstring m_key;
			std::wstring m_value;
		};

		class JsonBoolNode : public JsonNode<JsonBoolNode>
		{
		public:
			JsonBoolNode(const std::wstring& key, bool value);

			virtual const std::wstring& getKey() const;

			bool getInternalValue() const ;

		private:
			std::wstring m_key;
			bool m_value;
		};


		class JsonDoubleNode : public JsonNode<JsonDoubleNode>
		{
		public:
			JsonDoubleNode(const std::wstring& key, double value);

			virtual const std::wstring& getKey() const;

			double getInternalValue() const ;

		private:
			std::wstring m_key;
			double m_value;
		};

		class JsonNullNode : public JsonNode<JsonNullNode>
		{
		public:
			JsonNullNode(const std::wstring& value);

			virtual const std::wstring& getKey() const;

			NullType getInternalValue() const ;

		private:
			std::wstring m_key;
		};		

		template <typename T>
		struct JsonNodeValueImpl;

		template <>
		struct JsonNodeValueImpl<const JsonObjectNode*>
		{	
			static const JsonObjectNode* getValue(const IJsonNode* node)
			{
				const JsonObjectNode* tmpNode = static_cast<const JsonObjectNode*>(node);
				return tmpNode->getInternalValue();
			}
		};

		template <>
		struct JsonNodeValueImpl<const JsonArrayNode*>
		{	
			static const JsonArrayNode* getValue(const IJsonNode* node)
			{
				const JsonArrayNode* tmpNode = static_cast<const JsonArrayNode*>(node);
				return tmpNode->getInternalValue();
			}
		};

		template <>
		struct JsonNodeValueImpl<double>
		{	
			static double getValue(const const IJsonNode* node)
			{
				const JsonDoubleNode* tmpNode = static_cast<const JsonDoubleNode*>(node);
				return tmpNode->getInternalValue();
			}
		};

		template <>
		struct JsonNodeValueImpl<const std::wstring&>
		{	
			static const std::wstring& getValue(const IJsonNode* node)
			{
				const JsonStringNode* tmpNode = static_cast<const JsonStringNode*>(node);
				return tmpNode->getInternalValue();
			}
		};

		template <>
		struct JsonNodeValueImpl<bool>
		{	
			static bool getValue(const IJsonNode* node)
			{
				const JsonBoolNode* tmpNode = static_cast<const JsonBoolNode*>(node);
				return tmpNode->getInternalValue();
			}
		};

		template <>
		struct JsonNodeValueImpl<NullType>
		{	
			static NullType getValue(const IJsonNode* node)
			{
				const JsonNullNode* tmpNode = static_cast<const JsonNullNode*>(node);
				return tmpNode->getInternalValue();
			}
		};


		template <typename T>
		T JsonObjectNode::getValueByKey( const std::wstring& key ) const
		{

			stdext::hash_map<std::wstring, IJsonNode*>::const_iterator itr = m_childList.find(key);
			if (itr != m_childList.end())
			{
				const IJsonNode* childNode = static_cast<const IJsonNode*>(itr->second);
				if (childNode != NULL)
				{
					return JsonNodeValueImpl<T>::getValue(childNode);
				}
			}
			throw 1;
		}

		template <typename T>
		T JsonArrayNode::getValueByIndex( int index ) const
		{
			const IJsonNode* childNode = static_cast<const IJsonNode*>(m_childList[index]);
			if (childNode != NULL)
			{
				return JsonNodeValueImpl<T>::getValue(childNode);
			}
		}

	}
}
#endif //SIMFRAMEWORK_JSON_JSON_NODE_H